If
you consider the examples that have been shown, you may have missed the
importance of XAML containers. The most obvious of these can be seen in
the Grid element:
<UserControl x:Class="WinningTheLottery.Sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock Text="Hello" />
</Grid>
</UserControl>
The purpose of these
containers is to allow other elements to be laid out in particular ways
on the visual surface of Silverlight. The containers themselves
typically don’t have any user interface themselves but simply are used
to determine how different XAML elements are formatted on the screen.
There are a number of layout containers that are important to designing
in XAML. Each of these layout containers can take multiple child
elements and lay them out in specific ways. You can see the common
visual containers in Table 1.
Table 1. Visual Containers
Layout Container | Description | Supports Multiple Children? |
---|
Grid | Table-like layout of columns and rows. | Yes |
StackPanel | Horizontal or Vertical stacking of individual elements. | Yes |
Canvas | Position-based layout (via Top and Left position). | Yes |
ScrollViewer | Virtual container that can be larger than the contents to allow users to scroll through the container. | No |
Border | To create a simple border around a single element. | No |
These containers are
important as they are used to determine how your elements are laid out.
The most important of these is the Grid container and it will be the
one you use most often. The Grid is a container that supports dynamic,
table-like layout using rows and columns. To define rows and columns,
you set the Grid’s
<UserControl x:Class="WinningTheLottery.Sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Hello" />
</Grid>
</UserControl>
Creating new columns and rows is done using the ColumnDefinitions and RowDefintions
properties (as shown). This allows you to specify that individual
elements are in a particular row or column using the attached property
(see sidebar):
<UserControl x:Class="WinningTheLottery.Sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="Hello" Grid.Column="1" />
</Grid>
</UserControl>
By using the attached property, the TextBlock is indicating that the TextBlock
belongs in the second column (note that row and column numbers are zero
indexed). In this way the Grid is creating columns or rows proactively
by specifying the number of rows or columns up front. At first blush it
may seem verbose to create row and/or column definitions this way, but
its important as the definitions contain other important information
that can be set.
Some properties are not
relevant until a property except in some specific scopes. For example,
when an object is inside a Grid, being able to tell the XAML what
column or row you are in becomes critical. But that same element inside
a StackPanel has no notion of a row or column. Attached properties are
specific types of properties that are only valid in certain cases.
Attached Properties are defined by the name of the owning type and the
name of the attached property (e.g. Grid.Row). The information in
attached properties are available to the class that exposes them as
that is where they are typically used.
For example in the Grid
class, as the Grid lays out the elements inside of it, it will query
for the attached property to determine which row and/or column to place
an element. Literally the properties are attached at runtime so the
underlying element does not need to have unneeded properties (like Row
and Column).
|
When creating Rows and Columns, the Height or Width (respectively) can be defined in three ways as seen in Table 2.
Table 2. Grid Row and Column Sizing
Type | Description | Example |
---|
Auto | Sizes row or column based the contents. The size will be determined by the largest object in a respective row or column. | <RowDefinition Height=“Auto” /> |
Pixel | Set size of row and column to a specific size, in pixels. Larger objects will be clipped. | <RowDefinition Height=“100” /> |
Star | Proportionally sizes rows or columns based on the weighted value. | <RowDefinition Height=“*” /> <RowDefinition Height=“25*” /> <RowDefinition Height=“0.147*” />
|
Auto and pixel sized are
pretty self-explanatory, but the star sizing required some more
explanation. Star sized is a proportionally sized based on the values
in the height or width. For example:
<UserControl x:Class="WinningTheLottery.Sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="33*" />
<ColumnDefinition Width="66*" />
</Grid.ColumnDefinitions>
</Grid>
</UserControl>
The numbers in the Width
are used as weighted proportions of the whole size. While this looks
similar to using percentages (like you may be used to in web
applciations), the numbers are not part of an arbitrary 100% scale. For
example, changing the values to "1*" and "2*" will yield the same 2-to-1 ratio as "33*" and "66*". In the case of using a star alone (e.g. “*”), it is equivalent of "1*". By using Grid
elements with a mix of Auto, pixel and star sizing, you can create
elastic layouts (using star sizing for the flexible sized elements and
pixel/auto sizing for the more static parts of the design).
You have already seen that
you can use attached properties to set the row and/or column of a
specific element inside the Grid. The Grid class also supports being
able to specify RowSpan and ColumnSpan
to signify that a particular element should span more than one row
and/or column. This will give you extra flexibility to create your
table-based designs, like so:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="1" />
<TextBlock Text="1"
Grid.Row="1" />
<TextBlock Text="1"
Grid.Column="1" />
<TextBlock Text="Across All 3 Columns"
Grid.ColumnSpan="3" />
<TextBlock Text="Across Both Rows"
Grid.RowSpan="2" />
</Grid>
While you may use the other layout containers is certain cases, you should become comfortable with the Grid as it is the container you will use most often.